#!/usr/bin/env bb

(ns jank.test.ahead-of-time
  (:require
   [babashka.fs :as fs]
   [babashka.process :as proc]
   [clojure.string :as str]
   [clojure.test :as t :refer [deftest is testing use-fixtures]]))

(def this-nsym (ns-name *ns*))
(def default-output-file "target/cli")

(defn module-path [alias]
  (-> (proc/sh "clojure" (str "-A:" alias) "-Spath") :out str/trim))

(defn compile-command [module-path main-module {:keys [optimization-flag
                                                       output-file]
                                                :or {optimization-flag "-O0"
                                                     output-file default-output-file}}]
  (str "jank " optimization-flag
       " --module-path=" module-path
       " compile " main-module
       " -o " output-file))

(use-fixtures
  :each
  (fn [f]
    (fs/delete-tree "./target")
    (println "Cleaned up 'target'")
    (f)))

(deftest aot-single-jank-module
  (let [alias-name  "single-jank-module"
        module-path (module-path alias-name)]
    (doseq [main-with-args? [true false]
            :let [main-module (if main-with-args?
                                "main-with-args"
                                "main-without-args")
                  args (if main-with-args?
                         " foo bar baz"
                         nil)
                  expected-output (slurp (str "expected-output/" alias-name "/" main-module))
                  compile-command (compile-command module-path main-module {})]]
      (testing (str alias-name " & " main-module)
        (is (= 0 (->> compile-command
                      (proc/sh {:out *out*
                                :err *out*})
                      :exit)))
        (is (= expected-output (->> args (str default-output-file)
                                    proc/sh :out))))
      (testing "Executable in temp directory"
        (fs/with-temp-dir [dir {}]
          (fs/move default-output-file dir)
          (is (= expected-output (->> (str "./cli " args)
                                      (proc/sh {:dir dir})
                                      :out))))))))

(deftest aot-multiple-jank-modules
  (let [alias-name "only-jank-modules"
        module-path (module-path alias-name)
        module "core"
        args " Admin 3000"
        output-filename "target/my-cli"
        expected-output (slurp (str "expected-output/" alias-name "/" module))
        compile-command (compile-command module-path module {:output-file output-filename})]
    (testing (str alias-name " & core")
      (is (= 0 (->> compile-command
                    (proc/sh {:out *out*
                              :err *out*})
                    :exit)))
      (is (= expected-output (-> output-filename (str args)
                                 proc/sh :out))))))

(deftest aot-jank-and-cpp-modules
    (let [alias-name "jank-and-cpp-modules"
          module-path (module-path alias-name)
          module "core"
          args " foo bar"
          expected-output (slurp (str "expected-output/" alias-name "/" module))
          compile-command (compile-command module-path module {:include-headers? true})]
      (testing (str alias-name " & core")
        (println "Compile command: " compile-command)
        (is (= 0 (->> compile-command proc/sh :exit)))
        (is (= expected-output (-> default-output-file (str args)
                                   proc/sh :out))))))

(deftest aot-jank-and-cpp-modules-release-build
    (let [alias-name "jank-and-cpp-modules"
          module-path (module-path alias-name)
          module "core"
          args " foo bar"
          expected-output (slurp (str "expected-output/" alias-name "/" module))
          compile-command (compile-command module-path module {:include-headers? true
                                                               :optimization-flag "-O3"})]
      (testing (str alias-name " & core")
        (println "Compile command: " compile-command)
        (is (= 0 (->> compile-command
                      (proc/sh {:out *out*
                                :err *out*})
                      :exit)))
        (is (= expected-output (-> default-output-file (str args)
                                   proc/sh :out))))))

(deftest aot-cpp-raw-inline
  (let [alias-name "cpp-raw-inline"
        module-path (module-path alias-name)
        module "cpp-raw-inline.core"
        expected-output (slurp (str "expected-output/" alias-name "/core"))
        compile-command (compile-command module-path module {})]
    (testing (str alias-name " & core")
      (is (= 0 (->> compile-command
                    (proc/sh {:out *out*
                              :err *out*})
                    :exit)))
      (is (= expected-output (-> default-output-file proc/sh :out))))))

(defn -main []
  (when (empty? (System/getenv "JANK_SKIP_AOT_CHECK"))
    (proc/sh {:out *out* :err *out*} "jank check-health")
    (System/exit
      (if (t/successful? (t/run-tests this-nsym))
        0
        1))))

(when (= *file* (System/getProperty "babashka.file"))
  (apply -main *command-line-args*))
